<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>2048拼图游戏</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@3.4.27/dist/vue.global.min.js"></script>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            touch-action: manipulation;
        }
        
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
            padding: 20px;
            overflow: hidden;
        }
        
        .game-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            max-width: 480px;
            width: 100%;
            padding: 20px;
            background: rgba(255, 255, 255, 0.1);
            backdrop-filter: blur(10px);
            border-radius: 20px;
            box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3);
        }
        
        .header {
            text-align: center;
            margin-bottom: 20px;
            width: 100%;
        }
        
        h1 {
            font-size: 3.5rem;
            font-weight: 700;
            color: white;
            text-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
            letter-spacing: 2px;
            margin-bottom: 10px;
        }
        
        .scores-container {
            display: flex;
            justify-content: space-between;
            width: 100%;
            margin-bottom: 20px;
        }
        
        .score-box {
            background: rgba(255, 255, 255, 0.2);
            padding: 12px 20px;
            border-radius: 12px;
            text-align: center;
            min-width: 120px;
        }
        
        .score-title {
            color: #fff;
            font-size: 1rem;
            font-weight: 700;
            text-transform: uppercase;
        }
        
        .score-value {
            color: white;
            font-size: 2rem;
            font-weight: 700;
            margin-top: 4px;
        }
        
        .game-message {
            margin: 15px 0;
            padding: 10px;
            border-radius: 12px;
            background: rgba(0, 0, 0, 0.4);
            color: white;
            font-size: 1.2rem;
            font-weight: 600;
            text-align: center;
            opacity: 0;
            transition: all 0.3s ease;
        }
        
        .game-message.show {
            opacity: 1;
        }
        
        .grid-container {
            position: relative;
            width: 100%;
            aspect-ratio: 1/1; /* 确保正方形 */
            background: rgba(255, 255, 255, 0.1);
            border-radius: 12px;
            margin-bottom: 20px;
            overflow: hidden;
            -webkit-user-select: none;
            user-select: none;
        }
        
        .grid {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            grid-template-rows: repeat(4, 1fr);
            gap: 12px;
            padding: 12px;
        }
        
        .grid-cell {
            background: rgba(255, 255, 255, 0.15);
            border-radius: 8px;
        }
        
        .tile {
            position: absolute;
            display: flex;
            justify-content: center;
            align-items: center;
            font-weight: 800;
            border-radius: 8px;
            transition: all 0.15s ease;
            z-index: 10;
            font-size: 1.8rem;
            text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
            animation: appear 0.2s ease;
            -webkit-user-drag: none;
            user-select: none;
        }
        
        @keyframes appear {
            0% {
                transform: scale(0);
                opacity: 0;
            }
            100% {
                transform: scale(1);
                opacity: 1;
            }
        }
        
        .new-tile {
            animation: newTile 0.2s ease;
        }
        
        @keyframes newTile {
            0% { transform: scale(0.8); }
            50% { transform: scale(1.1); }
            100% { transform: scale(1); }
        }
        
        .merged-tile {
            animation: merged 0.2s ease;
        }
        
        @keyframes merged {
            0% { transform: scale(1); }
            50% { transform: scale(1.1); }
            100% { transform: scale(1); }
        }
        
        .tile-2 { background: #eee4da; color: #776e65; }
        .tile-4 { background: #eee1c9; color: #776e65; }
        .tile-8 { background: #f3b27a; color: white; }
        .tile-16 { background: #f69664; color: white; }
        .tile-32 { background: #f77c5f; color: white; }
        .tile-64 { background: #f75f3b; color: white; }
        .tile-128 { background: #edd073; color: white; font-size: 1.5rem; }
        .tile-256 { background: #edcc62; color: white; font-size: 1.5rem; }
        .tile-512 { background: #edc950; color: white; font-size: 1.5rem; }
        .tile-1024 { background: #edc53f; color: white; font-size: 1.3rem; }
        .tile-2048 { background: #edc22e; color: white; font-size: 1.3rem; }
        .tile-super { background: #3c3a33; color: white; font-size: 1.1rem; }
        
        .controls {
            display: flex;
            gap: 15px;
            width: 100%;
            justify-content: center;
            margin-top: 15px;
        }
        
        button {
            padding: 14px 28px;
            font-size: 1.1rem;
            font-weight: 700;
            border: none;
            border-radius: 12px;
            background: rgba(255, 255, 255, 0.2);
            color: white;
            cursor: pointer;
            transition: all 0.2s ease;
            text-transform: uppercase;
            letter-spacing: 1px;
            backdrop-filter: blur(5px);
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
        }
        
        button:hover {
            background: rgba(255, 255, 255, 0.3);
            transform: translateY(-3px);
            box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
        }
        
        button:active {
            transform: translateY(0);
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
        }
        
        .instructions {
            margin-top: 25px;
            text-align: center;
            color: rgba(255, 255, 255, 0.85);
            font-size: 1rem;
            line-height: 1.6;
            max-width: 80%;
        }
        
        .swipe-hint {
            display: none;
            position: absolute;
            bottom: 10px;
            left: 50%;
            transform: translateX(-50%);
            color: rgba(255, 255, 255, 0.7);
            font-size: 0.9rem;
        }
        
        @media (hover: none) {
            .swipe-hint {
                display: block;
            }
        }
        
        @media (max-width: 500px) {
            .header h1 {
                font-size: 3rem;
            }
            
            .score-box {
                padding: 10px 15px;
                min-width: 100px;
            }
            
            .score-value {
                font-size: 1.8rem;
            }
            
            .tile {
                font-size: 1.5rem;
            }
            
            .tile-128, .tile-256, .tile-512 {
                font-size: 1.2rem;
            }
            
            .tile-1024, .tile-2048 {
                font-size: 1rem;
            }
            
            button {
                padding: 12px 20px;
                font-size: 1rem;
            }
            
            .instructions p {
                font-size: 0.9rem;
            }
        }
    </style>
</head>
<body>
    <div id="app">
        <div class="game-container">
            <div class="header">
                <h1>2048</h1>
            </div>
            
            <div class="scores-container">
                <div class="score-box">
                    <div class="score-title">分数</div>
                    <div class="score-value">{{ score }}</div>
                </div>
                
                <div class="score-box">
                    <div class="score-title">最高分</div>
                    <div class="score-value">{{ highScore }}</div>
                </div>
            </div>
            
            <div class="game-message" :class="{ show: hasWon || gameOver }">
                <p v-if="hasWon">恭喜！你赢了！🎉</p>
                <p v-else-if="gameOver">游戏结束 😢</p>
                <p v-else>{{ message }}</p>
            </div>
            
            <div 
                class="grid-container"
                @touchstart="touchStart"
                @touchmove.prevent="touchMove"
                @touchend="touchEnd"
            >
                <div class="swipe-hint">滑动屏幕移动方块</div>
                <div class="grid">
                    <template v-for="row in boardSize" :key="row">
                        <div 
                            v-for="col in boardSize" 
                            :key="col" 
                            class="grid-cell"
                        ></div>
                    </template>
                </div>
                <div class="tiles-container">
                    <div 
                        v-for="tile in tiles" 
                        :key="tile.id"
                        :class="['tile', 'tile-' + tile.value, { 'new-tile': tile.new, 'merged-tile': tile.merged }]"
                        :style="{
                            width: tileSize + 'px',
                            height: tileSize + 'px',
                            top: (tile.y * (tileSize + gap) + gridOffset) + 'px',
                            left: (tile.x * (tileSize + gap) + gridOffset) + 'px',
                            zIndex: tile.value,
                            'font-size': fontScale(tile.value) + 'rem'
                        }"
                    >
                        {{ tile.value }}
                    </div>
                </div>
            </div>
            
            <div class="controls">
                <button @click="resetGame">新游戏</button>
                <button @click="undoMove">悔棋</button>
            </div>
            
            <div class="instructions">
                <p>使用键盘 <strong>← ↑ → ↓</strong> 方向键或<strong>触摸滑动</strong>移动方块<br>当两个相同数字的方块碰撞时，它们会合并成它们的和！</p>
            </div>
        </div>
    </div>

    <script>
        const { createApp, ref, reactive, computed, onMounted, onUnmounted } = Vue;
        
        createApp({
            setup() {
                const boardSize = 4;
                const gap = 12; // 方格间距
                const gridOffset = 12; // 内边距
                
                const tiles = reactive([]);
                const score = ref(0);
                const highScore = ref(0);
                const hasWon = ref(false);
                const gameOver = ref(false);
                const message = ref("");
                const prevBoardState = ref([]);
                const tileSize = ref(80);
                
                // 触屏控制变量
                const touchStartX = ref(0);
                const touchStartY = ref(0);
                const touchEndX = ref(0);
                const touchEndY = ref(0);
                
                const generateId = () => Date.now() + Math.random().toString(36).substring(2, 9);
                
                // 计算合适的方块尺寸
                const calculateTileSize = () => {
                    const container = document.querySelector('.grid-container');
                    if (!container) return 80;
                    
                    // 获取容器尺寸
                    const containerWidth = container.offsetWidth;
                    
                    // 计算可用空间 (减去内边距和间距)
                    const availableWidth = containerWidth - (2 * gridOffset) - ((boardSize - 1) * gap);
                    
                    // 计算每个方块的尺寸
                    return availableWidth / boardSize;
                };
                
                // 字体大小缩放
                const fontScale = (value) => {
                    if (value <= 64) return 1.8;
                    if (value <= 512) return 1.5;
                    if (value <= 2048) return 1.3;
                    return 1.1;
                };
                
                // 添加新方块
                const addNewTile = (value = Math.random() > 0.8 ? 4 : 2) => {
                    const emptyCells = [];
                    
                    // 找到所有空单元格
                    for (let y = 0; y < boardSize; y++) {
                        for (let x = 0; x < boardSize; x++) {
                            if (!tiles.some(tile => tile.x === x && tile.y === y)) {
                                emptyCells.push({x, y});
                            }
                        }
                    }
                    
                    if (emptyCells.length > 0) {
                        const cell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
                        tiles.push({
                            id: generateId(),
                            value: value,
                            x: cell.x,
                            y: cell.y,
                            new: true,
                            merged: false
                        });
                        
                        setTimeout(() => {
                            const newTile = tiles.find(t => t.id === tiles[tiles.length - 1].id);
                            if (newTile) newTile.new = false;
                        }, 200);
                    }
                    
                    return emptyCells.length > 0;
                };
                
                // 重置游戏
                const resetGame = () => {
                    tiles.splice(0, tiles.length);
                    score.value = 0;
                    hasWon.value = false;
                    gameOver.value = false;
                    message.value = "";
                    
                    // 添加初始两个方块
                    addNewTile();
                    addNewTile();
                    
                    // 更新本地存储最高分
                    try {
                        localStorage.setItem('2048_high_score', highScore.value.toString());
                    } catch (e) {
                        console.log("保存最高分失败:", e);
                    }
                };
                
                // 保存游戏状态
                const saveBoardState = () => {
                    prevBoardState.value = JSON.parse(JSON.stringify(tiles));
                };
                
                // 悔棋功能
                const undoMove = () => {
                    if (prevBoardState.value.length) {
                        tiles.splice(0, tiles.length, ...prevBoardState.value.map(tile => ({
                            ...tile,
                            merged: false,
                            new: false
                        })));
                        
                        // 移除最后一个方块（游戏结束时添加的）
                        if (gameOver.value && tiles.length > prevBoardState.value.length) {
                            tiles.pop();
                        }
                        
                        gameOver.value = false;
                        hasWon.value = false;
                        message.value = "";
                    }
                };
                
                // 检查是否能移动
                const canMove = () => {
                    // 检查是否还有空位
                    for (let y = 0; y < boardSize; y++) {
                        for (let x = 0; x < boardSize; x++) {
                            if (!tiles.some(tile => tile.x === x && tile.y === y)) {
                                return true;
                            }
                        }
                    }
                    
                    // 检查是否还能合并
                    for (let y = 0; y < boardSize; y++) {
                        for (let x = 0; x < boardSize; x++) {
                            const tile = tiles.find(t => t.x === x && t.y === y);
                            if (tile) {
                                const neighbors = [
                                    {x: x + 1, y},
                                    {x: x - 1, y},
                                    {x, y: y + 1},
                                    {x, y: y - 1}
                                ];
                                
                                for (const neighbor of neighbors) {
                                    if (neighbor.x >= 0 && neighbor.x < boardSize && neighbor.y >= 0 && neighbor.y < boardSize) {
                                        const neighborTile = tiles.find(t => t.x === neighbor.x && t.y === neighbor.y);
                                        if (neighborTile && neighborTile.value === tile.value) {
                                            return true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    
                    return false;
                };
                
                // 移动方块
                const move = (direction) => {
                    if (gameOver.value) return;
                    
                    saveBoardState();
                    
                    let moved = false;
                    const positions = calculatePositions(direction);
                    
                    // 清除合并标记
                    tiles.forEach(tile => tile.merged = false);
                    
                    // 按行/列顺序处理
                    positions.order.forEach(index => {
                        const line = tiles.filter(tile => positions.getKey(tile) === index);
                        line.sort((a, b) => positions.sort(a, b));
                        
                        let pos = positions.start;
                        let prevTile = null;
                        
                        for (const tile of line) {
                            // 检查是否可合并
                            if (prevTile && prevTile.value === tile.value && !prevTile.merged) {
                                prevTile.value += tile.value;
                                prevTile.merged = true;
                                
                                // 更新分数
                                score.value += prevTile.value;
                                
                                // 更新最高分
                                if (score.value > highScore.value) {
                                    highScore.value = score.value;
                                    try {
                                        localStorage.setItem('2048_high_score', highScore.value.toString());
                                    } catch (e) {
                                        console.log("保存最高分失败:", e);
                                    }
                                }
                                
                                // 检查是否获胜
                                if (prevTile.value === 2048 && !hasWon.value) {
                                    hasWon.value = true;
                                    message.value = "你赢了！继续挑战更高分吧！";
                                }
                                
                                // 移除当前方块
                                const tileIndex = tiles.indexOf(tile);
                                tiles.splice(tileIndex, 1);
                                moved = true;
                            } else {
                                if (positions.getValue(tile) !== pos) moved = true;
                                positions.setValue(tile, pos);
                                prevTile = tile;
                                pos += positions.step;
                            }
                        }
                    });
                    
                    if (moved) {
                        // 添加新方块
                        const added = addNewTile();
                        
                        // 检查游戏是否结束
                        if (!added && !canMove()) {
                            gameOver.value = true;
                            message.value = "游戏结束，无法移动了！";
                        }
                    }
                };
                
                // 方向计算逻辑
                const calculatePositions = (direction) => {
                    const operations = {
                        left: {
                            getKey: (tile) => tile.y,
                            getValue: (tile) => tile.x,
                            setValue: (tile, value) => tile.x = value,
                            sort: (a, b) => a.x - b.x,
                            start: 0,
                            step: 1,
                            order: [0, 1, 2, 3]
                        },
                        right: {
                            getKey: (tile) => tile.y,
                            getValue: (tile) => tile.x,
                            setValue: (tile, value) => tile.x = value,
                            sort: (a, b) => b.x - a.x,
                            start: 3,
                            step: -1,
                            order: [0, 1, 2, 3]
                        },
                        up: {
                            getKey: (tile) => tile.x,
                            getValue: (tile) => tile.y,
                            setValue: (tile, value) => tile.y = value,
                            sort: (a, b) => a.y - b.y,
                            start: 0,
                            step: 1,
                            order: [0, 1, 2, 3]
                        },
                        down: {
                            getKey: (tile) => tile.x,
                            getValue: (tile) => tile.y,
                            setValue: (tile, value) => tile.y = value,
                            sort: (a, b) => b.y - a.y,
                            start: 3,
                            step: -1,
                            order: [0, 1, 2, 3]
                        }
                    };
                    
                    return operations[direction];
                };
                
                // 处理键盘输入
                const handleKeyDown = (e) => {
                    switch (e.key) {
                        case 'ArrowUp':
                            move('up');
                            e.preventDefault();
                            break;
                        case 'ArrowDown':
                            move('down');
                            e.preventDefault();
                            break;
                        case 'ArrowLeft':
                            move('left');
                            e.preventDefault();
                            break;
                        case 'ArrowRight':
                            move('right');
                            e.preventDefault();
                            break;
                        case 'r': case 'R': // 添加重置快捷键
                            resetGame();
                            break;
                    }
                };
                
                // 处理触屏事件
                const touchStart = (e) => {
                    const touch = e.touches[0];
                    touchStartX.value = touch.clientX;
                    touchStartY.value = touch.clientY;
                };
                
                const touchMove = (e) => {
                    e.preventDefault();
                };
                
                const touchEnd = (e) => {
                    const touch = e.changedTouches[0];
                    touchEndX.value = touch.clientX;
                    touchEndY.value = touch.clientY;
                    
                    handleTouchMove();
                };
                
                // 计算触摸方向
                const handleTouchMove = () => {
                    const dx = touchEndX.value - touchStartX.value;
                    const dy = touchEndY.value - touchStartY.value;
                    const absDx = Math.abs(dx);
                    const absDy = Math.abs(dy);
                    
                    // 最小滑动阈值
                    if (Math.max(absDx, absDy) < 30) return;
                    
                    if (absDx > absDy) {
                        if (dx > 0) move('right');
                        else move('left');
                    } else {
                        if (dy > 0) move('down');
                        else move('up');
                    }
                };
                
                // 窗口大小变化时重新计算方块尺寸
                const handleResize = () => {
                    tileSize.value = calculateTileSize();
                };
                
                // 组件挂载时
                onMounted(() => {
                    // 初始化尺寸
                    tileSize.value = calculateTileSize();
                    
                    // 监听窗口变化
                    window.addEventListener('resize', handleResize);
                    
                    // 监听键盘事件
                    window.addEventListener('keydown', handleKeyDown);
                    
                    // 尝试从localStorage加载最高分
                    try {
                        const savedHighScore = localStorage.getItem('2048_high_score');
                        if (savedHighScore) {
                            highScore.value = parseInt(savedHighScore);
                        }
                    } catch (e) {
                        console.log("加载最高分失败:", e);
                    }
                    
                    // 初始化游戏
                    resetGame();
                });
                
                // 组件卸载时
                onUnmounted(() => {
                    window.removeEventListener('resize', handleResize);
                    window.removeEventListener('keydown', handleKeyDown);
                });
                
                return {
                    boardSize,
                    tileSize,
                    tiles,
                    score,
                    highScore,
                    hasWon,
                    gameOver,
                    message,
                    gap,
                    gridOffset,
                    resetGame,
                    undoMove,
                    touchStart,
                    touchMove,
                    touchEnd,
                    fontScale
                };
            }
        }).mount('#app');
    </script>
</body>
</html>